﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Transactions;
using Common.Logging;
using NPMonitor.Configuration;
using NPMonitor.DataModel;
using NPMonitor.Helpers;
using NPMonitor.Implementation.Internal;
using NPMonitor.Interfaces;

namespace NPMonitor.Implementation.Store
{
    public class JobStore : IJobStore2
    {
        #region Properties & Members

        private ISettingsProvider settings;
        private ILog logger;
        // private Random randomizer;
        private string connectionString;
        private TimeSpan transactionTimeout;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Linq2SqlJobStore"/> class.
        /// </summary>
        public JobStore()
            : this(
            ConfigurationSettings.SettingsProvider,
            LogManager.GetCurrentClassLogger(),
            (IEFJobStoreSettingsProvider)Utils.CreateInstanceWithRequiredInterface(ConfigurationSettings.SettingsProvider.JobStoreSettingsProviderType.AssemblyQualifiedName, "IEFJobStoreSettingsProvider"))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Linq2SqlJobStore"/> class.
        /// </summary>
        /// <param name="settingsProvider">The settings provider.</param>
        /// <param name="loggingProvider">The logging provider.</param>
        /// <param name="jobStoreSettingsProvider">The job store settings provider.</param>
        public JobStore(ISettingsProvider settingsProvider, ILog logger, IEFJobStoreSettingsProvider jobStoreSettingsProvider)
        {
            connectionString = jobStoreSettingsProvider.ConnectionString;
            transactionTimeout = jobStoreSettingsProvider.TransactionLockTimeout;

            // randomizer = new Random((int)Helpers.Utils.CurrentServerUtcTime.TimeOfDay.TotalSeconds);
            this.logger = logger;
            settings = settingsProvider;
        }

        #endregion

        #region Job Operations

        public JobData CreateJob(Type jobType, Guid fileUniqueId, string data, string metaData, byte queueId, ISchedule schedule = null, Guid? uniqueId = null, string name = null, string description = null, string application = null, string group = null, TimeSpan? absoluteTimeout = null, JobStatus? jobStatus = null, DateTime? createdDate = null, bool suppressHistory = false, bool deleteWhenDone = false)
        {
            throw new NotImplementedException();
        }

        public JobData CreateJob(JobData jobData, CalendarSchedule schedule)
        {
            using (var ctx = new DbStoreContext())
            {
                if (null == jobData.CreatedDate)
                {
                    jobData.CreatedDate = Helpers.Utils.CurrentServerUtcTime;
                }

                var strSchedule = schedule != null ? Utils.SerializeObject(schedule, schedule.GetType()) : null; ;

                var strScheduleType = schedule != null ? schedule.GetType().AssemblyQualifiedName : null;

                var nextStartTime = schedule != null ? schedule.GetNextOccurrence() : null;

                jobData.Schedule = strSchedule;

                jobData.ScheduleType = strScheduleType;

                jobData.NextStartTime = nextStartTime;

                ctx.Entry(jobData).State = EntityState.Added;
                ctx.Jobs.Add(jobData);

                ctx.SaveChanges();
            }

            return jobData;
        }

        public bool DeleteJob(long jobId, bool deleteHistory = false)
        {
            using (var ctx = new DbStoreContext())
            {
                if (deleteHistory == true)
                {
                    var subEntities = ctx.JobExecutionHistory.Where(h => h.JobId == jobId);

                    foreach (var subEntity in subEntities)
                    {
                        ctx.Entry(subEntity).State = EntityState.Deleted;
                        ctx.JobExecutionHistory.Remove(subEntity);
                    }
                }

                var entity = ctx.Jobs.Find(jobId);

                ctx.Entry(entity).State = EntityState.Deleted;
                ctx.Jobs.Remove(entity);

                ctx.SaveChanges();

                return true;
            }
        }

        public bool DeleteJob(JobData job, bool deleteHistory = false)
        {
            using (var ctx = new DbStoreContext())
            {
                if (deleteHistory == true)
                {
                    var subEntities = ctx.JobExecutionHistory.Where(h => h.JobId == job.Id);

                    foreach (var subEntity in subEntities)
                    {
                        ctx.Entry(subEntity).State = EntityState.Deleted;
                        ctx.JobExecutionHistory.Remove(subEntity);
                    }
                }

                ctx.Entry(job).State = System.Data.EntityState.Deleted;
                ctx.Jobs.Remove(job);

                ctx.SaveChanges();

                return true;
            }
        }

        public bool UpdateJob(JobData job)
        {
            using (var ctx = new DbStoreContext())
            {
                ctx.Entry(job).State = EntityState.Modified;

                ctx.SaveChanges();

                return true;
            }
        }

        public void RefreshJobStatus()
        {
            UpdateJobForRunning();
            CancelScheduledJobs();
        }

        public void UpdateJobForRunning()
        {
            using (var ctx = new DbStoreContext())
            {
                var query = from p in ctx.Jobs
                            where p.StatusId == (int)JobStatus.Pending
                            select p;

                var list = query.ToList();

                foreach (var job in list)
                {
                    var machine = ctx.Machines.Where(t => t.MachineName == job.MachineName).FirstOrDefault();

                    if (machine.Connectivity == Connectivities.Connected.ToString())
                    {
                        if (job.Schedule == null)
                        {
                            job.StatusId = (int)JobStatus.Ready;
                        }
                        else
                        {
                            job.StatusId = (int)JobStatus.Scheduled;
                        }

                        ctx.Entry(job).State = EntityState.Modified;
                        ctx.SaveChanges();
                    }
                }
            }
        }

        private void CancelScheduledJobs()
        {
            using (var ctx = new DbStoreContext())
            {
                var query = from p in ctx.Jobs
                            where p.StatusId == (int)JobStatus.Scheduled
                            select p;
                var list = query.ToList();
                foreach (var job in list)
                {
                    var machine = ctx.Machines.Where(t => t.MachineName == job.MachineName).FirstOrDefault();
                    if (machine.Connectivity == Connectivities.Unconnected.ToString())
                    {
                        job.StatusId = (int)JobStatus.Pending;
                        ctx.Entry(job).State = EntityState.Modified;
                        ctx.SaveChanges();
                    }
                }
            }
        }

        public ReadOnlyCollection<JobData> QueueReadyJobs(byte? queueId, uint take, string machineName)
        {
            List<JobData> jobs = new List<JobData>();

            using (var ctx = new DbStoreContext())
            {
                try
                {
                    var storedJobs = ctx.Jobs.Where(j => j.StatusId == (int)JobStatus.Ready
                        && (queueId == null || j.QueueId == queueId)
                        && j.MachineName == machineName)
                        .OrderBy(j => j.NextStartTime).Take((int)take).ToList();

                    foreach (var storedJob in storedJobs)
                    {
                        storedJob.Status = JobStatus.Queuing;
                        ctx.Entry(storedJob).State = EntityState.Modified;

                        jobs.Add(storedJob);
                    }

                    ctx.SaveChanges();
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format("Failed to submit changes to the database."), ex);
                }
            }

            return jobs.AsReadOnly();
        }

        public ReadOnlyCollection<JobData> GetJobs(uint skip = 0, uint take = 1, Guid[] jobUniqueIds = null, long[] jobIds = null, JobStatus[] jobStatuses = null, byte[] queueIds = null, string[] machineNames = null, string[] typeNames = null, string[] applications = null, string[] groups = null)
        {
            using (var ctx = new DbStoreContext())
            {
                try
                {
                    var query = ctx.Jobs.AsQueryable();

                    if (jobUniqueIds != null && jobUniqueIds.Length > 0)
                    {
                        query = query.Where(j => jobUniqueIds.Contains(j.UniqueId));
                    }

                    if (jobIds != null && jobIds.Length > 0)
                    {
                        query = query.Where(j => jobIds.Contains(j.Id));
                    }

                    if (jobStatuses != null && jobStatuses.Length > 0)
                    {
                        int[] jobStatusIds = jobStatuses.Select(s => (int)s).ToArray();

                        query = query.Where(j => jobStatusIds.Contains(j.StatusId));
                    }

                    if (queueIds != null && queueIds.Length > 0)
                    {
                        query = query.Where(j => queueIds.Select(q => (int)q).Contains(j.QueueId));
                    }

                    if (machineNames != null && machineNames.Length > 0)
                    {
                        query = query.Where(j => machineNames.Contains(j.MachineName));
                    }

                    if (applications != null && applications.Length > 0)
                    {
                        query = query.Where(j => applications.Contains(j.Application));
                    }

                    if (groups != null && groups.Length > 0)
                    {
                        query = query.Where(j => groups.Contains(j.Group));
                    }

                    var storedJobs = query.OrderBy(j2 => j2.LastStartTime).Skip((int)skip).Take((int)take).ToList();

                    return storedJobs.AsReadOnly();
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format("Failed to get jobs from the database."), ex);
                }
            }

            return null;
        }

        #region Job Status Operations

        public bool PauseJob(long[] jobIds, string errorMessage = null)
        {
            if (jobIds == null || jobIds.Length == 0) return false;

            using (var ctx = new DbStoreContext())
            {
                try
                {
                    var storedJobs = ctx.Jobs.Where(j => jobIds.Contains(j.Id)).ToList();

                    foreach (var storedJob in storedJobs)
                    {
                        if (storedJob.Status == JobStatus.Pending
                                        || storedJob.Status == JobStatus.Scheduled
                                        || storedJob.Status == JobStatus.Ready
                                        || storedJob.Status == JobStatus.ExecutionTimeout
                                        || storedJob.Status == JobStatus.Executing)
                        {
                            // 在这里清空下次执行时间
                            storedJob.NextStartTime = null;

                            if (errorMessage != null)
                            {
                                storedJob.LastErrorMessage = errorMessage;
                            }

                            ctx.Entry(storedJob).State = EntityState.Modified;
                        }
                    }

                    ctx.SaveChanges();

                    return true;
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format("Failed to submit changes to the database."), ex);
                }
            }

            return false;
        }

        #endregion

        public bool SetJobStatuses(long[] jobIds, JobStatus? oldStatus, JobStatus newStatus, string errorMessage = null, string instance = "Not Specified")
        {
            if (jobIds == null || jobIds.Length == 0) return true;

            using (var ctx = new DbStoreContext())
            {
                try
                {
                    foreach (long id in jobIds)
                    {
                        SetJobStatuses(id, oldStatus, newStatus, errorMessage);
                    }
                    return true;
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format("Failed to submit changes to the database."), ex);
                }
            }

            return false;
        }

        public bool SetJobStatuses(long jobId, JobStatus? oldStatus, JobStatus newStatus, string errorMessage = null)
        {
            bool updateStatus = true;

            using (var ctx = new DbStoreContext())
            {
                try
                {
                    //Unfortunately, can't use a single update statement here, as we need to know the exact records that were updated to trigger the requiresaction event.
                    JobData storedJob = ctx.Jobs.FirstOrDefault(j => j.Id == jobId);

                    if (oldStatus.HasValue ? storedJob.Status == oldStatus : true)
                    {
                        //如果当前状态为删除，则不能改为其它状态
                        if (storedJob.Status == JobStatus.Deleted)
                        {
                            updateStatus = false;
                        }
                        else
                        {
                            //Only Pending, Scheduled, Ready, Runing Timeout,Done could be changed to Paused,
                            if (newStatus == JobStatus.Paused)
                            {
                                if (storedJob.Status == JobStatus.Pending
                                    || storedJob.Status == JobStatus.Scheduled
                                    || storedJob.Status == JobStatus.Ready
                                    || storedJob.Status == JobStatus.ExecutionTimeout
                                    || storedJob.Status == JobStatus.Executing
                                    || storedJob.Status == JobStatus.Done
                                    )
                                {
                                    // 在这里清空下次执行时间
                                    storedJob.NextStartTime = null;
                                }
                                else
                                {
                                    updateStatus = false;
                                }
                            }
                            else if (newStatus == JobStatus.Pending)
                            {
                                // 只有暂停的\ShutdownTimeout\失败的，可以改为pending状态
                                if (storedJob.Status == JobStatus.Paused || storedJob.Status == JobStatus.Fail || storedJob.Status == JobStatus.ExecutionTimeout || storedJob.Status == JobStatus.Done)
                                {
                                    // 清空重试次数
                                    storedJob.RetryTimes = 0;

                                    // 在这里计算下次执行时间
                                    if (storedJob.Schedule != null)
                                    {
                                        storedJob.NextStartTime = storedJob.JobSchedule.GetNextOccurrence();
                                    }
                                }
                                else if (storedJob.Status != JobStatus.ShutdownTimeout)
                                {
                                    updateStatus = false;
                                }
                            }
                            else if (newStatus == JobStatus.ExecutionTimeout)
                            {
                                // 清空重试次数
                                storedJob.RetryTimes = 0;

                                if (storedJob.LastEndTime == null)
                                {
                                    storedJob.LastEndTime = Utils.CurrentServerUtcTime;
                                }

                                // 如果Timeout,在这里计算下次执行时间
                                if (storedJob.Schedule != null)
                                {
                                    storedJob.NextStartTime = storedJob.JobSchedule.GetNextOccurrence();
                                }
                            }
                            else if (newStatus == JobStatus.Deleted)
                            {
                                if (storedJob.Status == JobStatus.Executing)
                                {
                                    updateStatus = false;
                                }
                                else
                                {
                                    //如果删除，则更改IsDelete字段
                                    storedJob.IsDelete = true;
                                }
                            }
                            else if (newStatus == JobStatus.Scheduled)
                            {
                                // 如果Timeout,在这里计算下次执行时间
                                if (storedJob.Schedule != null)
                                {
                                    storedJob.NextStartTime = storedJob.JobSchedule.GetNextOccurrence();
                                }
                            }
                        }
                    }
                    if (updateStatus)
                    {
                        storedJob.Status = newStatus;
                    }

                    if (errorMessage != null)
                    {
                        storedJob.LastErrorMessage = errorMessage;
                    }
                    ctx.Entry(storedJob).State = EntityState.Modified;
                    ctx.SaveChanges();

                    return updateStatus;
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format("Failed to submit changes to the database."), ex);
                }
            }
            return updateStatus;
        }


        public void DequeueJob(JobData job)
        {
            using (var ctx = new DbStoreContext())
            {
                try
                {
                    if (job.Id != 0 && (job.Status == JobStatus.Queuing || job.Status == JobStatus.Queued))
                    {
                        job.Status = JobStatus.Ready;
                        ctx.Entry(job).State = EntityState.Modified;

                        ctx.SaveChanges();
                    }
                }
                catch (Exception ex)
                {
                    logger.Error("DequeueJob failed.", ex);
                }
            }
        }

        public ReadOnlyCollection<JobData> GetScheduleReadyJobs(string machineName, DateTime nextExecutionStartDateTimeAtOrBefore)
        {
            using (var ctx = new DbStoreContext())
            {
                try
                {
                    var storedJobs = ctx.Jobs.Where(j =>
                        (j.StatusId == (int)JobStatus.Scheduled || j.StatusId == (int)JobStatus.ExecutionTimeout || j.StatusId == (int)JobStatus.FailRetry)
                        && j.MachineName == machineName
                        && j.Schedule != null
                        && j.NextStartTime <= nextExecutionStartDateTimeAtOrBefore).OrderBy(j => j.NextStartTime);

                    return storedJobs.ToList().AsReadOnly();
                }
                catch (Exception ex)
                {
                    logger.Error("GetScheduleReadyJobs failed.", ex);
                }
            }

            return null;
        }

        public JobExecutionHistory CreateJobExecutionHistory(JobData job, Guid historyUniqueId)
        {
            using (var ctx = new DbStoreContext())
            {
                DateTime now = Helpers.Utils.CurrentServerUtcTime;

                JobExecutionHistory jobHistory = new JobExecutionHistory
                {
                    AbsoluteTimeout = job.AbsoluteTimeout,
                    HistoryUniqueId = historyUniqueId,
                    TaskId = job.TaskId.Value,
                    TaskType = job.TaskType,
                    CreatedDate = job.CreatedDate,
                    Instance = job.Instance ?? job.MachineName,
                    MachineName = job.MachineName,
                    JobUniqueId = job.UniqueId,
                    Data = job.Data,
                    EndTime = job.LastEndTime ?? now,
                    ErrorMessage = job.LastErrorMessage,
                    JobId = job.Id,
                    MetaData = job.MetaData,
                    QueueId = job.QueueId,
                    StartTime = job.LastStartTime ?? now,
                    Status = job.Status,
                    Success = job.Status == JobStatus.Done,
                    Application = job.Application,
                    Group = job.Group,
                    Name = job.Name,
                    Description = job.Description,
                    ErrorCode = job.ErrorCode
                };

                ctx.Entry(jobHistory).State = EntityState.Added;
                ctx.SaveChanges();

                return jobHistory;
            }
        }

        public ReadOnlyCollection<JobExecutionHistory> GetJobExecutionHistories(uint skip = 0, uint take = 1, long[] jobHistoryIds = null, Guid[] jobUniqueIds = null, long[] jobIds = null, JobStatus[] jobStatuses = null, byte[] queueIds = null, string[] machineNames = null, string[] typeNames = null, string[] applications = null, string[] groups = null)
        {
            using (var ctx = new DbStoreContext())
            {
                try
                {
                    //In order to get a table lock, we have to resort to SQL.  Yeah, I said table lock.  Want to get the real actual values here.
                    var query = ctx.JobExecutionHistory.AsQueryable();

                    if (jobHistoryIds != null && jobHistoryIds.Length > 0)
                    {
                        query = query.Where(j => jobHistoryIds.Contains(j.Id));
                    }
                    if (jobUniqueIds != null && jobUniqueIds.Length > 0)
                    {
                        query = query.Where(j => jobUniqueIds.Contains(j.JobUniqueId));
                    }
                    if (jobIds != null && jobIds.Length > 0)
                    {
                        query = query.Where(j => jobIds.Contains(j.JobId));
                    }
                    if (jobStatuses != null && jobStatuses.Length > 0)
                    {
                        query = query.Where(j => jobStatuses.Contains((JobStatus)j.StatusId));
                    }
                    if (queueIds != null && queueIds.Length > 0)
                    {
                        query = query.Where(j => queueIds.Select(q => (int)q).Contains(j.QueueId));
                    }
                    if (machineNames != null && machineNames.Length > 0)
                    {
                        query = query.Where(j => machineNames.Contains(j.MachineName));
                    }
                    if (typeNames != null && typeNames.Length > 0)
                    {
                        query = query.Where(j => typeNames.Contains(j.JobType));
                    }
                    if (applications != null && applications.Length > 0)
                    {
                        query = query.Where(j => applications.Contains(j.Application));
                    }
                    if (groups != null && groups.Length > 0)
                    {
                        query = query.Where(j => groups.Contains(j.Group));
                    }

                    var history = query.OrderByDescending(h => h.Id).Skip((int)skip).Take((int)take).ToList();

                    return history.AsReadOnly();
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format("Failed to get jobs from the database."), ex);
                }
            }

            return null;
        }

        public ReadOnlyCollection<Alert> GetAlerts(uint skip = 0, uint take = 1)
        {
            using (var ctx = new DbStoreContext())
            {
                try
                {
                    var storedAlerts = ctx.Alerts.OrderBy(a => a.Id).Skip((int)skip).Take((int)take);

                    return storedAlerts.ToList().AsReadOnly();
                }
                catch (Exception ex)
                {
                    logger.Error("GetAlerts failed.", ex);
                }
            }
            return null;
        }

        public Alert CreateAlert(long jobId, long? jobHistoryId, string message)
        {
            using (var ctx = new DbStoreContext())
            {
                try
                {
                    Alert alert = new Alert { JobId = jobId, JobHistoryId = jobHistoryId, Message = message };

                    ctx.Entry(alert).State = EntityState.Added;
                    ctx.Alerts.Add(alert);

                    ctx.SaveChanges();
                }
                catch (Exception ex)
                {
                    logger.Error("CreateAlert failed.", ex);
                }
            }

            return null;
        }

        public bool DeleteAlerts(long[] ids = null)
        {
            using (var ctx = new DbStoreContext())
            {
                var entities = ctx.Alerts.Where(e => ids.Contains(e.Id));

                foreach (var entity in entities)
                {
                    ctx.Entry(entity).State = EntityState.Deleted;
                    ctx.Alerts.Remove(entity);
                }

                ctx.SaveChanges();

                return true;
            }
        }

        /// <summary>
        /// 根据机器名获取Active Scheduled jobs
        /// </summary>
        /// <param name="nextExecutionStartDateTimeAtOrBefore"></param>
        /// <returns></returns>
        public ReadOnlyCollection<JobData> GetJobsByMachineName(string machineName, int pageIndex, int pageSizes, out int total)
        {
            using (var ctx = new DbStoreContext())
            {
                try
                {
                    if (pageIndex < 0)
                    {
                        pageIndex = 0;
                    }
                    if (pageSizes < 0)
                    {
                        pageSizes = int.MaxValue;
                    }
                    int scheduledStatusId = (int)JobStatus.Deleted;

                    var jobDatas = ctx.Jobs.Where(j =>
                        j.StatusId != scheduledStatusId
                        && j.MachineName.Trim() == machineName.Trim()).OrderByDescending(j => j.CreatedDate).ToList();
                    total = jobDatas.Count();

                    jobDatas = jobDatas.Skip(pageIndex * pageSizes).Take(pageSizes).ToList();

                    return jobDatas.AsReadOnly();
                }
                catch (Exception ex)
                {
                    logger.Error("GetScheduleReadyJobs failed.", ex);
                    throw;
                }
            }
        }

        /// <summary>
        /// 根据机器名获取Scheduled job的总数(删除状态除外）
        /// </summary>
        /// <param name="nextExecutionStartDateTimeAtOrBefore"></param>
        /// <returns></returns>
        public int GetActiveScheduleJobsTotalCountByMachineName(string machineName)
        {
            using (var ctx = new DbStoreContext())
            {
                try
                {
                    int deletedStatusId = (int)JobStatus.Deleted;

                    return ctx.Jobs.Where(j => j.StatusId != deletedStatusId
                        && j.MachineName.Trim() == machineName.Trim()).Count();
                }
                catch (Exception ex)
                {
                    logger.Error("GetScheduleReadyJobs failed.", ex);
                }
            }
            return 0;
        }

        public int GetDeletedJobCount(int jobId)
        {
            using (var ctx = new DbStoreContext())
            {
                return ctx.Jobs.Where(x => x.TaskId == jobId && x.IsDelete == false).Count();
            }
        }

        public List<string> GetJobScheduleByID(int id)
        {
            List<string> list = new List<string>();
            string result = string.Empty;

            JobData scheduleJob = new JobData();

            using (var ctx = new DbStoreContext())
            {
                scheduleJob = ctx.Jobs.FirstOrDefault(d => d.Id == id);
            }

            if (scheduleJob != null)
            {
                if (scheduleJob.Schedule != null)
                {
                    string scheduleDays = "";
                    CalendarSchedule schedule = Utils.DeserializeObject<CalendarSchedule>(scheduleJob.Schedule.ToString());

                    var weekList = schedule.DaysOfWeek.ToList();

                    foreach (var w in weekList)
                    {
                        scheduleDays += (w.ToString().Substring(0, 3) + "." + " ");
                    }
                    var startTime = schedule.DailyStartTime.AsTimeSpan.ToString();
                    var repeatEvery = schedule.RepeatInterval.ToString();
                    list.Add(scheduleDays);
                    list.Add(startTime);
                    list.Add(repeatEvery);
                }
            }

            return list;
        }

        /// <summary>
        /// Get Scheduled Task count by task id
        /// </summary>
        /// <param name="taskId"></param>
        /// <returns></returns>
        public List<JobData> GetScheduledTaskById(int taskId)
        {
            using (var ctx = new DbStoreContext())
            {
                return ctx.Jobs.Where(x => x.TaskId == taskId).ToList();
            }
        }

        #endregion

        public JobExecutionHistory GetJobExecutionHistory(int historyId)
        {
            using (var ctx = new DbStoreContext())
            {
                return ctx.JobExecutionHistory.Where(t => t.Id == historyId).FirstOrDefault();
            }
        }

        public ReadOnlyCollection<JobExecutionHistory> GetJobExecutionHistories(string machineName, int pageIndex, int pageSizes, string jobName, Nullable<JobStatus> status, DateTime startTime, DateTime endTime, out int total)
        {
            using (var ctx = new DbStoreContext())
            {
                try
                {
                    if (pageIndex < 0)
                    {
                        pageIndex = 0;
                    }
                    if (pageSizes < 0)
                    {
                        pageSizes = int.MaxValue;
                    }

                    var query = from p in ctx.JobExecutionHistory
                                where p.StartTime >= startTime && p.StartTime <= endTime
                                select p;

                    if (!string.IsNullOrEmpty(machineName))
                    {
                        query = query.Where(t => t.MachineName == machineName);
                    }
                    if (null != jobName && jobName != string.Empty)
                    {
                        query = query.Where(t => t.Name.Contains(jobName));
                    }
                    if (null != status)
                    {
                        query = query.Where(t => t.StatusId == (int)status);
                    }

                    total = query.Count();

                    var queryPage = query.OrderByDescending(x => x.Id).Skip(pageIndex * pageSizes).Take(pageSizes);

                    return queryPage.ToList().AsReadOnly();
                }
                catch (Exception ex)
                {
                    logger.Error("GetJobExecutionHistories failed.", ex);
                    throw ex;
                }
            }
        }

        public List<JobData> CreateJobs(List<JobData> joblist, CalendarSchedule schedule)
        {
            List<JobData> jobs = new List<JobData>();

            using (var trans = new TransactionScope())
            {
                foreach (var job in joblist)
                {
                    jobs.Add(CreateJob(job, schedule));
                }

                trans.Complete();
            }
            return jobs;
        }


        public JobData GetTaskInfoById(int id)
        {
            using (var ctx = new DbStoreContext())
            {
                return ctx.Jobs.FirstOrDefault(x => x.Id == id);
            }
        }
    }
}
